#include "bga/window/Window.h"
#include <SFML/Window.hpp>
#include "bga/window/WindowListener.h"
#include "bga/window/KeyListener.h"
#include "bga/window/MouseListener.h"
#include <iostream>

using namespace std;
using namespace sf;

namespace bga
{


Window::Window(const std::string &title, unsigned int width, unsigned int height, int depth, bool fullscreen)
{
    /// We can't do elseway. The declaration of sf::Window doesn't have to appear in .h
    if(fullscreen)
    {
        this->window = new sf::Window(VideoMode::GetFullscreenModes()[0], title, sf::Style::Fullscreen);
    }
    else
    {
        this->window = new sf::Window(sf::VideoMode(width, height, depth), title, sf::Style::Close);
    }



    //this->window->UseVerticalSync(true);

    //this->window->SetFramerateLimit(60);

}

Window::~Window()
{
    if(this->window)
    {
        if(this->window->IsOpened())
        {
            this->close();
        }
        delete this->window;
        this->window = NULL;
    }
}

void Window::handleEvent()
{
    sf::Event event;
    if(this->window->PollEvent(event))
    {
        switch(event.Type)
        {
            case sf::Event::Closed:
            case sf::Event::Resized:
            case sf::Event::LostFocus:
            case sf::Event::GainedFocus:
                this->handleWindowEvent(event);
                break;

            case sf::Event::KeyPressed:
            case sf::Event::KeyReleased:
                this->handleKeyEvent(event);
                break;

            case sf::Event::MouseButtonPressed:
            case sf::Event::MouseButtonReleased:
                this->handleMouseEvent(event);
                break;

            case sf::Event::MouseWheelMoved:
                this->handleMouseWheelEvent(event);
                break;

            case sf::Event::MouseMoved:
                this->handleMouseMotionEvent(event);
                break;

            case sf::Event::MouseEntered:
                break;

            case sf::Event::MouseLeft:
                break;

            default:
                break;
        }
    }
}

void Window::display()
{
    this->window->Display();
}

void Window::close() const
{
    window->Close();
}

bool Window::isOpened() const
{
    return window->IsOpened();
}

void Window::setCursorPosition(unsigned int x, unsigned int y)
{
    this->window->SetCursorPosition(x, y);
}

unsigned int Window::getCenterX() const
{
    return this->window->GetWidth() / 2;
}

unsigned int Window::getCenterY() const
{
    return this->window->GetHeight() / 2;
}

unsigned int Window::getWidth() const
{
    return this->window->GetWidth();
}

unsigned int Window::getHeight() const
{
    return this->window->GetHeight();
}

unsigned int Window::getSizeStencilBuffer() const
{
    return this->window->GetSettings().StencilBits;
}

void Window::handleWindowEvent(const sf::Event& event)
{
    WindowEvent windowEvent(*this);
    for(unsigned int i = 0; i < windowListeners.size(); i++)
    {
        switch(event.Type)
        {
            case sf::Event::Closed:
                this->windowListeners[i]->windowClosed(windowEvent);
                break;

            case sf::Event::Resized:
                this->windowListeners[i]->windowResized(windowEvent);
                break;

            case sf::Event::LostFocus:
                this->windowListeners[i]->windowLostFocus(windowEvent);
                break;

            case sf::Event::GainedFocus:
                this->windowListeners[i]->windowGainFocus(windowEvent);
                break;

            default:
                break;


        }
    }
}

void Window::handleKeyEvent(const sf::Event& event)
{
    KeyEvent keyEvent(*this);
    Key::Code key = Key::K_NONE;
    switch(event.Key.Code)
    {
        case sf::Key::A:
            key = Key::K_A;
            break;
        case sf::Key::B:
            key = Key::K_B;
            break;
        case sf::Key::C:
            key = Key::K_C;
            break;
        case sf::Key::D:
            key = Key::K_D;
            break;
        case sf::Key::E:
            key = Key::K_E;
            break;
        case sf::Key::F:
            key = Key::K_F;
            break;
        case sf::Key::G:
            key = Key::K_G;
            break;
        case sf::Key::H:
            key = Key::K_H;
            break;
        case sf::Key::I:
            key = Key::K_I;
            break;
        case sf::Key::J:
            key = Key::K_J;
            break;
        case sf::Key::K:
            key = Key::K_K;
            break;
        case sf::Key::L:
            key = Key::K_L;
            break;
        case sf::Key::M:
            key = Key::K_M;
            break;
        case sf::Key::N:
            key = Key::K_N;
            break;
        case sf::Key::O:
            key = Key::K_O;
            break;
        case sf::Key::P:
            key = Key::K_P;
            break;
        case sf::Key::Q:
            key = Key::K_Q;
            break;
        case sf::Key::R:
            key = Key::K_R;
            break;
        case sf::Key::S:
            key = Key::K_S;
            break;
        case sf::Key::T:
            key = Key::K_T;
            break;
        case sf::Key::U:
            key = Key::K_U;
            break;
        case sf::Key::V:
            key = Key::K_V;
            break;
        case sf::Key::W:
            key = Key::K_W;
            break;
        case sf::Key::X:
            key = Key::K_X;
            break;
        case sf::Key::Y:
            key = Key::K_Y;
            break;
        case sf::Key::Z:
            key = Key::K_Z;
            break;
        case sf::Key::Num0:
        case sf::Key::Numpad0:
            key = Key::K_0;
            break;
        case sf::Key::Num1:
        case sf::Key::Numpad1:
            key = Key::K_1;
            break;
        case sf::Key::Num2:
        case sf::Key::Numpad2:
            key = Key::K_2;
            break;
        case sf::Key::Num3:
        case sf::Key::Numpad3:
            key = Key::K_3;
            break;
        case sf::Key::Num4:
        case sf::Key::Numpad4:
            key = Key::K_4;
            break;
        case sf::Key::Num5:
        case sf::Key::Numpad5:
            key = Key::K_5;
            break;
        case sf::Key::Num6:
        case sf::Key::Numpad6:
            key = Key::K_6;
            break;
        case sf::Key::Num7:
        case sf::Key::Numpad7:
            key = Key::K_7;
            break;
        case sf::Key::Num8:
        case sf::Key::Numpad8:
            key = Key::K_8;
            break;
        case sf::Key::Num9:
        case sf::Key::Numpad9:
            key = Key::K_9;
            break;
        case sf::Key::Up:
            key = Key::K_UP;
            break;
        case sf::Key::Down:
            key = Key::K_DOWN;
            break;
        case sf::Key::Right:
            key = Key::K_RIGHT;
            break;
        case sf::Key::Left:
            key = Key::K_LEFT;
            break;
        case sf::Key::Escape:
            key = Key::K_ESCAPE;
        default:
            break;
    }
    keyEvent.setKeyCode(key);
    for(unsigned int i = 0; i < this->keyListeners.size(); i++)
    {
        switch(event.Type)
        {
            case sf::Event::KeyPressed:
                this->keyListeners[i]->keyPressed(keyEvent);
                break;

            case sf::Event::KeyReleased:
                this->keyListeners[i]->keyReleased(keyEvent);
                break;

            default:
                break;
        }
    }
}

void Window::handleMouseEvent(const sf::Event& event)
{
    MouseEvent mouseEvent(*this);
    bga::MouseButton::Code mouseButton = MouseButton::NONE;
    switch(event.MouseButton.Button)
    {
        case sf::Mouse::Left:
            mouseButton = bga::MouseButton::LEFT;
            break;
        case sf::Mouse::Middle:
            mouseButton = MouseButton::MIDDLE;
            break;
        case sf::Mouse::Right:
            mouseButton = MouseButton::RIGHT;
            break;
        default:
            break;
    }

    mouseEvent.setMouseButton(mouseButton);
    mouseEvent.setX(event.MouseButton.X);
    mouseEvent.setY(event.MouseButton.Y);

    for(unsigned int i = 0; i < this->mouseListeners.size(); i++)
    {
        switch(event.Type)
        {
            case sf::Event::MouseButtonPressed:
                this->mouseListeners[i]->mouseButtonPressed(mouseEvent);
                break;

            case sf::Event::MouseButtonReleased:
                this->mouseListeners[i]->mouseButtonReleased(mouseEvent);
                break;

            default:
                break;
        }
    }
}

void Window::handleMouseMotionEvent(const sf::Event& event)
{
    MouseEvent mouseEvent(*this);

    mouseEvent.setX(event.MouseMove.X);
    mouseEvent.setY(event.MouseMove.Y);
    mouseEvent.setMouseButton(MouseButton::NONE);

    for(unsigned int i = 0; i < this->mouseListeners.size(); i++)
    {
        this->mouseListeners[i]->mouseMoved(mouseEvent);
    }
}

void Window::handleMouseWheelEvent(const sf::Event& event)
{
    MouseEvent mouseEvent(*this);

    mouseEvent.setMouseButton(MouseButton::NONE);

    mouseEvent.setDeltaWheel(event.MouseWheel.Delta);

    for(unsigned int i = 0; i < this->mouseListeners.size(); i++)
    {
        this->mouseListeners[i]->mouseWheelMoved(mouseEvent);
    }
}

void Window::addWindowListener(bga::WindowListener& listener)
{
    this->windowListeners.push_back(&listener);
    //cout << listener << endl;
}

void Window::addKeyListener(bga::KeyListener& listener)
{
    this->keyListeners.push_back(&listener);
}

void Window::addMouseListener(bga::MouseListener& listener)
{
    this->mouseListeners.push_back(&listener);
}

/*void Window::addMouseMotionListener(const bga::MouseMotionListener& listener)
{
    this->MouseMotionListeners.push_back(&listener);
}*/

}
